package com.aohj.patchmaster.popup.actions;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FilenameFilter;
import java.util.ArrayList;
import java.util.List;

import javax.xml.parsers.DocumentBuilderFactory;

import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IFolder;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.jface.action.IAction;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.TreeSelection;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.ui.IActionDelegate;
import org.eclipse.ui.IObjectActionDelegate;
import org.eclipse.ui.IWorkbenchPart;
import org.w3c.dom.Document;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.NodeList;

import com.aohj.patchmaster.Files;
import com.aohj.patchmaster.Message;
import com.aohj.patchmaster.PatchMaster;

public class PatchAction implements IObjectActionDelegate {
	private Shell shell;
	private TreeSelection selection = null;

	/**
	 * @see IObjectActionDelegate#setActivePart(IAction, IWorkbenchPart)
	 */
	@Override
	public void setActivePart(IAction action, IWorkbenchPart targetPart) {
		shell = targetPart.getSite().getShell();
	}

	/**
	 * @see IActionDelegate#selectionChanged(IAction, ISelection)
	 */
	@Override
	public void selectionChanged(IAction action, ISelection selection) {
		if (selection instanceof TreeSelection) {
			this.selection = (TreeSelection) selection;
		}
	}

	/**
	 * @see IActionDelegate#run(IAction)
	 */
	public void run(IAction action) {
		String title = Message.CREATE_PATCH_TITLE;
		String message = Message.CREATE_PATCH_MESSAGE;
		IFolder patchRoot = null;
		try {
			if (!(selection.getFirstElement() instanceof IFile))
				return;
			IFile file = (IFile) selection.getFirstElement();
			IProject project = Files.getProject(file);
			IFolder patchMasterRoot = Files
					.newFolder(project, PatchMaster.ROOT);
			File patchPathFile = Files.toFile(file);
			if (!patchPathFile.getName().equals(PatchMaster.PATCH_PATH))
				return;
			List<String> patchPaths = Files.readAllLines(patchPathFile,
					PatchMaster.CHARSET);
			if (patchPaths.size() == 0)
				return;
			String patchName = "";
			if (patchPathFile.getParentFile().getParentFile().getAbsolutePath()
					.equals(Files.getAbsolutePath(patchMasterRoot))) {
				String prefix = PatchMaster.newPrefix();
				patchName = patchPathFile.getParentFile().getName();
				if (!patchName.startsWith(prefix))
					patchName = prefix + patchName.substring(prefix.length());
			} else {
				patchName = PatchMaster.getProjectPatchName(Files
						.getName(project));
			}
			for (;;) {
				if (Files.exists(Files.getFolder(patchMasterRoot, patchName)))
					patchName = PatchMaster.getProjectRepatchName(patchName);
				else
					break;
			}
			patchRoot = Files.newFolder(patchMasterRoot, patchName);
			Files.newFile(
					Files.getFile(patchRoot, Files.toFile(file).getName()),
					Files.toFile(file));
			List<String> projectSrcPaths = new ArrayList<String>();
			String projectOutputPath = null;
			Document document = DocumentBuilderFactory
					.newInstance()
					.newDocumentBuilder()
					.parse(new File(Files.getAbsolutePath(project),
							".classpath"));
			NodeList nodeList = document.getElementsByTagName("classpathentry");
			for (int i = 0; i < nodeList.getLength(); i++) {
				NamedNodeMap attributes = nodeList.item(i).getAttributes();
				if ("src"
						.equals(attributes.getNamedItem("kind").getNodeValue())) {
					projectSrcPaths.add(attributes.getNamedItem("path")
							.getNodeValue());
				} else if ("output".equals(attributes.getNamedItem("kind")
						.getNodeValue())) {
					projectOutputPath = attributes.getNamedItem("path")
							.getNodeValue();
				}
			}
			for (String path : patchPaths) {
				if (path.startsWith("#") || "".equals(path.trim()))
					continue;
				path = path.replace("\\", PatchMaster.FILE_SEPARATOR);
				patch(patchRoot, path);
				int index = path.indexOf(PatchMaster.FILE_SEPARATOR);
				if (index == -1
						|| !projectSrcPaths.contains(path.substring(0, index))) {
					continue;
				}
				path = projectOutputPath + path.substring(index);
				if (!path.endsWith(".java")) {
					patch(patchRoot, path);
					continue;
				}
				path = path.substring(0, path.lastIndexOf(".")) + ".class";
				patch(patchRoot, path);
				File classFile = new File(Files.getAbsolutePath(project), path);
				final String innerOrEnumClassNamePrefix = classFile.getName()
						.substring(0, classFile.getName().lastIndexOf("."))
						+ "$";
				String[] innerOrEnumClassNames = classFile.getParentFile()
						.list(new FilenameFilter() {
							@Override
							public boolean accept(File dir, String name) {
								if (new File(dir, name).isFile()
										&& name.startsWith(innerOrEnumClassNamePrefix)
										&& name.endsWith(".class")) {
									return true;
								}
								return false;
							}
						});
				if (innerOrEnumClassNames.length == 0)
					continue;
				String basePath = path.substring(0,
						path.lastIndexOf(PatchMaster.FILE_SEPARATOR) + 1);
				for (String innerOrEnumClassName : innerOrEnumClassNames) {
					path = basePath + innerOrEnumClassName;
					patch(patchRoot, path);
				}
			}

			if (PatchMaster.COMPRESS) {
				for (IResource resource : Files.members(patchRoot)) {
					if (resource instanceof IFolder) {
						Files.refreshLocal(Files.getFile(patchRoot, Files
								.newZip(Files.toFile(resource)).getName()));
						Files.delete(resource);
					}
				}
			}
		} catch (Exception e) {
			Files.delete(patchRoot);
			message = String.valueOf(e);
		}
		MessageDialog.openInformation(shell, title, message);
	}

	private void patch(IFolder patchFolder, String path) throws CoreException,
			FileNotFoundException {
		String[] folders = new String[] {};
		String fileName = path;
		int lastIndex = path.lastIndexOf(PatchMaster.FILE_SEPARATOR);
		if (lastIndex != -1) {
			folders = path.substring(0, lastIndex).split(
					PatchMaster.FILE_SEPARATOR);
			fileName = path.substring(lastIndex);
		}
		IFolder folder = patchFolder;
		for (String s : folders) {
			folder = Files.newFolder(folder, s);
		}
		Files.newFile(Files.getFile(folder, fileName),
				new File(Files.getProjectAbsolutePath(patchFolder), path));
	}
}